home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 January: Mac OS SDK / Dev.CD Jan 99 SDK2.toast / Development Kits / TEC 1.4 / SampleCode / DropEncoder / Source / Encoder.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-09-25  |  11.1 KB  |  359 lines  |  [TEXT/CWIE]

  1. /*************************************************************************************
  2. #
  3. #    Encoder.c
  4. #    
  5. #     The guts of the text encoder sample.  This reads a text file, attempts to determine
  6. #   the encoding, and then converts it to a different encoding.
  7. #
  8. #    Author: Timothy Carroll
  9. #    Apple Developer Technical Support
  10. #    timc@apple.com
  11. #
  12. #    Revision: Jason Yeo
  13. #
  14. #    Modification History: 
  15. #
  16. #    2/9/97        TMC     Initial Release
  17. #
  18. #    9/12/97        JY         Updated for:
  19. #                        TEC 1.2.1 
  20. #                        Universal Interfaces 3.0
  21. #                        CodeWarrior 11 projects
  22. #
  23. #    Copyright © 1997 Apple Computer, Inc., All Rights Reserved
  24. #
  25. #
  26. #    You may incorporate this sample code into your applications without
  27. #    restriction, though the sample code has been provided "AS IS" and the
  28. #    responsibility for its operation is 100% yours.  However, what you are
  29. #    not permitted to do is to redistribute the source as "DSC Sample Code"
  30. #    after having made changes. If you're going to re-distribute the source,
  31. #    we require that you make it clear in the source that the code was
  32. #    descended from Apple Sample Code, but that you've made changes.
  33. #
  34. *************************************************************************************/
  35.  
  36. #include "Encoder.h"
  37. #include "TextCommon.h"
  38. #include "TextEncodingConverter.h"
  39. #include "Errors.h"
  40. #include <Files.h>
  41.  
  42. #define kOutputBufferSize 1024*32
  43.  
  44. OSStatus ReadFile (FSSpec *file, Ptr *bufferPtr, SInt32 *length);
  45. OSStatus FindBestEncoding (Ptr buffer, SInt32 length, TextEncoding *bestEncoding);
  46.  
  47. OSStatus EncodeFile (FSSpec *inputFile)
  48. {
  49.     OSStatus         theErr = noErr;
  50.     OSStatus        convertStatus = noErr;
  51.     
  52.     short            outputRef = 0;
  53.     Boolean            outputFileOpened = false;
  54.     FSSpec             outputFile;
  55.     short            volume;
  56.     
  57.     Ptr                inputBuffer = NULL, outputBuffer = NULL;
  58.     long            length;
  59.     TextEncoding    inputEncoding;
  60.     TECObjectRef    converter = NULL;
  61.     char            nameIndex;
  62.  
  63.     ByteCount         inputLength;
  64.     ByteCount         actualInputLength, actualOutputLength;
  65.     TextPtr            inputPtr;
  66.     
  67.     
  68.     // STEP 1 READ THE FILE
  69.     theErr = ReadFile (inputFile, &inputBuffer, &length);
  70.     FAIL_OSERR (theErr, "\pError: Failed to read the file");
  71.         
  72.     // STEP 2 SNIFF FOR ENCODING
  73.     theErr = FindBestEncoding (inputBuffer, length, &inputEncoding);
  74.     FAIL_OSERR (theErr, "\pError: Failed to find a good encoding")
  75.     
  76.     // STEP 3 CREATE A CONVERTER
  77.     theErr = TECCreateConverter (&converter, inputEncoding, gPreferences.outputEncoding);
  78.     FAIL_OSERR (theErr, "\pError: Unable to create a converter between the two encodings")
  79.     
  80.     // STEP 4 CREATE AN OUTPUT FILE
  81.     // For now, we delete any existing file with the name.  Our name is the old name, with
  82.     // .OUT appended to the end.  We cut short the name if it would be greater than 31 chars.
  83.     
  84.     outputFile = *inputFile;
  85.     nameIndex = outputFile.name[0];
  86.     
  87.     nameIndex += 4;
  88.     if (nameIndex > 31)
  89.         nameIndex = 31;
  90.         
  91.     outputFile.name[0] = nameIndex;
  92.     
  93.     nameIndex -= 3;
  94.     outputFile.name[nameIndex] = '.';
  95.     outputFile.name[nameIndex+1] = 'O';
  96.     outputFile.name[nameIndex+2] = 'U';
  97.     outputFile.name[nameIndex+3] = 'T';
  98.     
  99.     (void) FSpDelete (&outputFile);
  100.     
  101.     theErr = FSpCreate (&outputFile, 'DENC', 'TEXT', smSystemScript);
  102.     FAIL_OSERR (theErr, "\pError: Failed to create the output file");
  103.     
  104.     theErr = FSpOpenDF (&outputFile, fsRdWrPerm, &outputRef);
  105.     FAIL_OSERR (theErr, "\pError: Unable to open the output file")
  106.     outputFileOpened = true;
  107.     
  108.     theErr = SetEOF (outputRef, 0);
  109.     FAIL_OSERR (theErr, "\pError: Failed to reset the output file's length")
  110.     
  111.     theErr = SetFPos (outputRef, fsFromStart, 0);
  112.     FAIL_OSERR (theErr, "\pError: Unable to move to start of file")
  113.  
  114.     // First we need an output buffer
  115.     
  116.     outputBuffer = NewPtrClear (kOutputBufferSize);
  117.     theErr = MemError();
  118.     FAIL_OSERR (theErr, "\pError: Failed to create the output buffer")
  119.     FAIL_NIL (outputBuffer, "\pError: Failed to create the output buffer")
  120.     
  121.     
  122.     // STEP 5 CONVERT THE FILE
  123.     // We iterate over the input buffer, converting blocks of text into the
  124.     // output buffer and copying those files out to the output file.
  125.     
  126.     inputLength = length;
  127.     inputPtr = (unsigned char *) inputBuffer;
  128.     
  129.     do
  130.     {
  131.         convertStatus = TECConvertText (converter, inputPtr, inputLength, &actualInputLength,
  132.                                         (unsigned char *) outputBuffer, kOutputBufferSize, &actualOutputLength);
  133.  
  134.         // Advance the counters
  135.         inputPtr += actualInputLength;
  136.         inputLength -= actualInputLength;
  137.         
  138.         theErr = FSWrite (outputRef, (long *) &actualOutputLength, outputBuffer);
  139.         FAIL_OSERR( theErr, "\pError: Failed to write output data")
  140.         
  141.     } while (convertStatus == kTECUsedFallbacksStatus);
  142.     
  143.     do
  144.     {
  145.         convertStatus = TECFlushText (converter, (unsigned char *) outputBuffer, 
  146.                             kOutputBufferSize, &actualOutputLength);
  147.                             
  148.         theErr = FSWrite (outputRef, (long *) &actualOutputLength, outputBuffer);
  149.         FAIL_OSERR( theErr, "\pError: Failed to write output data")            
  150.     }
  151.     while (convertStatus == kTECNeedFlushStatus);
  152.     
  153.     theErr = GetVRefNum (outputRef, &volume);
  154.     FAIL_OSERR (theErr, "\pError: Failed to retrieve the volume ref num")
  155.     
  156.     theErr = FlushVol (NULL, volume);
  157.     FAIL_OSERR (theErr, "\pError: Failed to flush the volume of the output file") 
  158.     
  159.     // we're done!!
  160.     // Close everything and exit.
  161.     
  162.     goto cleanup; 
  163.     
  164. error:
  165.     if (theErr == noErr)
  166.         theErr = paramErr;
  167.         
  168. cleanup:
  169.     if (outputFileOpened)
  170.         (void) FSClose (outputRef);
  171.     if (inputBuffer)
  172.         DisposePtr (inputBuffer);
  173.     if (outputBuffer)
  174.         DisposePtr (outputBuffer);
  175.     if (converter)
  176.         (void) TECDisposeConverter (converter);
  177.     
  178.     return theErr;
  179. }
  180.  
  181.  
  182. // This routine simple opens up the file and reads the entire contents into a data buffer.
  183. OSStatus ReadFile (FSSpec *file, Ptr *bufferPtr, SInt32 *length)
  184. {
  185.     OSStatus         theErr = noErr;
  186.     short            fileRef;
  187.     Boolean            fileOpened = false;
  188.     Ptr                fileBuffer = NULL;
  189.     long            actualLength;
  190.     
  191.     theErr = FSpOpenDF (file, fsRdPerm, &fileRef);
  192.     FAIL_OSERR (theErr, "\pError: Unable to open the file")
  193.     fileOpened = true;
  194.     
  195.     theErr = GetEOF (fileRef, length);
  196.     FAIL_OSERR (theErr, "\pError: Unable to get the file's length")
  197.     
  198.     theErr = SetFPos (fileRef, fsFromStart, 0);
  199.     FAIL_OSERR (theErr, "\pError: Unable to move to start of file")
  200.     
  201.     fileBuffer = NewPtrClear (*length);
  202.     theErr = MemError();
  203.     FAIL_OSERR (theErr, "\pError: Failed to create a buffer to hold the file data")
  204.     FAIL_NIL (fileBuffer, "\pError: Failed to create a buffer to hold the file data")
  205.     
  206.     actualLength = *length;
  207.     theErr = FSRead(fileRef, &actualLength, fileBuffer);
  208.     FAIL_OSERR (theErr, "\pError: Failed to read the file")
  209.     if (actualLength != *length) SIGNAL_ERROR ("\pError: Incorrect length of info read")    
  210.     
  211.     *bufferPtr = fileBuffer;
  212.         
  213.     goto cleanup; // we're done, close all the files and exit.
  214.     
  215. error:
  216.     if (theErr == noErr)
  217.         theErr = paramErr;
  218.     
  219.     if (fileBuffer)
  220.         DisposePtr (fileBuffer);
  221.     
  222.     // We returned an error, so we should not return any valid buffer info.
  223.     *bufferPtr = NULL;
  224.     *length = 0;
  225.     
  226. cleanup:
  227.     
  228.     if (fileOpened)
  229.         (void) FSClose (fileRef);
  230.         
  231.     return theErr;
  232. }
  233.  
  234.  
  235. OSStatus FindBestEncoding (Ptr buffer, SInt32 length, TextEncoding *bestEncoding)
  236. {
  237.     OSStatus                theErr = noErr;
  238.     ItemCount                maxRegionEncodings, actualRegionEncodings;
  239.     ItemCount                maxSnifferEncodings, actualSnifferEncodings;
  240.     TextEncoding            *regionEncodings = NULL, *snifferEncodings = NULL;
  241.     
  242.     TECSnifferObjectRef     sniffer = NULL;
  243.     ItemCount                *errors = NULL, *features = NULL;
  244.     
  245.     int                        loop, loop2;
  246.     Boolean                    found;
  247.     
  248.     // Create a buffer that holds all the encodings for the region
  249.     
  250.     theErr = TECCountWebTextEncodings (gPreferences.locale, &maxRegionEncodings);
  251.     FAIL_OSERR (theErr, "\pError: Failed to retrieve count of encodings")
  252.     
  253.     regionEncodings = (TextEncoding *) NewPtrClear (sizeof (TextEncoding) * maxRegionEncodings);
  254.     theErr = MemError();
  255.     FAIL_OSERR (theErr, "\pError: Failed to allocate a buffer to hold encodings")
  256.     FAIL_NIL (regionEncodings, "\pError: Failed to allocate a buffer to hold encodings")
  257.     
  258.     theErr = TECGetWebTextEncodings (gPreferences.locale, regionEncodings, 
  259.                                         maxRegionEncodings, &actualRegionEncodings);
  260.     FAIL_OSERR (theErr, "\pError: Failed to retrieve list of encodings")
  261.     
  262.     
  263.     // Create a buffer that holds all of the encodings we can sniff
  264.     
  265.     theErr = TECCountAvailableSniffers (&maxSnifferEncodings);
  266.     FAIL_OSERR (theErr, "\pError: Failed to retrieve count of encodings")
  267.     
  268.     snifferEncodings = (TextEncoding *) NewPtrClear (sizeof (TextEncoding) * maxSnifferEncodings);
  269.     theErr = MemError();
  270.     FAIL_OSERR (theErr, "\pError: Failed to allocate a buffer to hold encodings")
  271.     FAIL_NIL (snifferEncodings, "\pError: Failed to allocate a buffer to hold encodings")
  272.     
  273.     theErr = TECGetAvailableSniffers (snifferEncodings, maxSnifferEncodings, &actualSnifferEncodings);
  274.     FAIL_OSERR (theErr, "\pError: Failed to retrieve list of encodings")
  275.     
  276.     
  277.     // Remove all encodings from the region list that don't exist in the sniffer list.
  278.     // After each iteration of the loop, we either advance the loop counter or shrink
  279.     // the list by one.
  280.     
  281.     loop = 0;
  282.     while (loop < actualRegionEncodings)
  283.     {
  284.         // Scan the list to see if we've found a match
  285.         found = false;
  286.         for (loop2 = 0; loop2 < actualSnifferEncodings; loop2++)
  287.         {
  288.             if (regionEncodings[loop] == snifferEncodings[loop2])
  289.             {
  290.                 found = true;
  291.                 loop2 = actualSnifferEncodings; // break out of loop
  292.             }
  293.         }    
  294.  
  295.         // Did we find a match?  If so, the region code lives
  296.         if (found)
  297.             loop++;
  298.         else
  299.         // If not, the region code dies, and we shrink the list. We decrement
  300.         // the list by one, and copy the last item on the list to the current
  301.         // loop location.  We do this even if we're the last item on the list...
  302.         // it won't be used later, and no reason to do a compare.
  303.         {
  304.             actualRegionEncodings -=1;
  305.             regionEncodings[loop] = regionEncodings[actualRegionEncodings];
  306.         }
  307.     }
  308.     
  309.     if (actualRegionEncodings == 0)
  310.         SIGNAL_ERROR ("\pError: Unable to find a sniffer that works for this region")
  311.     
  312.     // Create a sniffer object for our list of encodings.
  313.     theErr = TECCreateSniffer (&sniffer, regionEncodings, actualRegionEncodings);
  314.     FAIL_OSERR (theErr, "\pError: Unable to create a sniffer object.")
  315.     
  316.     // Create buffers to hold the return results of the sniffer
  317.     
  318.     errors = (ItemCount *) NewPtrClear (sizeof (ItemCount) * actualRegionEncodings);
  319.     theErr = MemError();
  320.     FAIL_OSERR (theErr, "\pError: Failed to allocate a buffer to hold sniffer errors")
  321.     FAIL_NIL (errors, "\pError: Failed to allocate a buffer to hold sniffer errors")
  322.     
  323.     features = (ItemCount *) NewPtrClear (sizeof (ItemCount) * actualRegionEncodings);
  324.     theErr = MemError();
  325.     FAIL_OSERR (theErr, "\pError: Failed to allocate a buffer to hold sniffer features")
  326.     FAIL_NIL (features, "\pError: Failed to allocate a buffer to hold sniffer features")
  327.     
  328.     // Finally, we try to find out the actual encoding!!!
  329.     
  330.     theErr = TECSniffTextEncoding (sniffer, (unsigned char *)buffer, length, 
  331.                                     regionEncodings, actualRegionEncodings,
  332.                                     errors, kMaxErrors,
  333.                                     features, kMaxFeatures);
  334.     FAIL_OSERR (theErr, "\pError: Failed to examine the encoding")
  335.     
  336.     // We made it this far. The first encoding is our best guess, so return it and cleanup;
  337.     *bestEncoding = *regionEncodings;
  338.     
  339.     goto cleanup;
  340.     
  341. error:
  342.     if (theErr == noErr)
  343.         theErr = paramErr;
  344.         
  345. cleanup:
  346.     if (sniffer)
  347.         TECDisposeSniffer(sniffer);
  348.     if (regionEncodings)
  349.         DisposePtr ((Ptr) regionEncodings);
  350.     if (snifferEncodings)
  351.         DisposePtr ((Ptr) snifferEncodings);
  352.     if (errors)
  353.         DisposePtr ((Ptr) errors);
  354.     if (features)
  355.         DisposePtr ((Ptr) features);
  356.  
  357.     return theErr;    
  358. }
  359.